//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.ManagementStudioModels
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Drawing;
    using System.IO;
    using System.Linq;
    using System.Windows;
    using System.Windows.Media.Imaging;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.eResearch.Common.Linq;
    using SR = Microsoft.Research.DataLayer;

    /// <summary>
    /// The middle layer representation of an activity.
    /// </summary>
    public class ActivityViewModel : BaseRegMgrModel
    {
        #region Private variables

        private bool isDeleted;
        
        private string author;

        private string parentBreadcrumb;

        private string iconPath;

        private Image icon;

        private BitmapImage registryImage;

        private BitmapImage savedImage;

        private BitmapImage previewImage;

        private static Dictionary<Image, BitmapImage> imageToBitmapDictionary = new Dictionary<Image, BitmapImage>();

        private static BitmapImage defaultImage;

        private string keywords;

        private ObservableCollection<ParameterModel> inputParameters = new ObservableCollection<ParameterModel>();

        private ObservableCollection<ParameterModel> outputParameters = new ObservableCollection<ParameterModel>();

        #endregion

        #region Constructors

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="workflow">The registry workflow object.</param>
        public ActivityViewModel(SR.Activity activity, CategoryViewModel parentCategoryModel)
        {
            this.Id = ((SR.IObject)activity).ID;
            this.Name = activity.Name;
            this.Author = activity.Author;
            this.Label = string.IsNullOrEmpty(activity.Label) ? activity.Name : activity.Label;
            this.Description = activity.Description;
            this.CreatedBy = activity.Created.User;
            this.CreatedDate = activity.Created.When;
            this.LastModifiedTime = activity.Updated.When;
            this.isDeleted = activity.IsDeleted;
            this.LastModifiedBy = activity.Updated.User;
            this.LastModifiedTime = activity.Updated.When;
            this.Icon = activity.Icon;
            this.SavedImage = ActivityViewModel.BitmapImageFromSDCtrl(activity.Icon);
            this.Keywords = activity.Keywords;
            if (parentCategoryModel != null)
            {
                this.Parent = parentCategoryModel;
            }

            this.IsLatest = activity.IsLatestVersion();
            this.Version = activity.Version;

            activity.ActivityParameters
                .Where(item => item.Direction == SR.DirectionType.Input)
                .ForEach(item => this.InputParameters.Add(new ParameterModel(item)));

            activity.ActivityParameters
                .Where(item => item.Direction == SR.DirectionType.Output)
                .ForEach(item => this.OutputParameters.Add(new ParameterModel(item)));
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ActivityViewModel"/> class.
        /// </summary>
        /// <param name="activity">The activity.</param>
        public ActivityViewModel(ActivityViewModel activityModel)
        {
            if (activityModel != null)
            {
                this.Id = activityModel.Id;
                this.Name = activityModel.Name;
                this.Author = activityModel.Author;
                this.Label = activityModel.Label;
                this.Description = activityModel.Description;
                this.CreatedBy = activityModel.CreatedBy;
                this.CreatedDate = activityModel.CreatedDate;
                this.LastModifiedTime = activityModel.LastModifiedTime;
                this.isDeleted = activityModel.IsDeleted;
                this.LastModifiedBy = activityModel.LastModifiedBy;
                this.LastModifiedTime = activityModel.LastModifiedTime;
                this.Parent = activityModel.Parent;
                this.Icon = activityModel.Icon;
                this.Keywords = activityModel.Keywords;
                this.SavedImage = activityModel.SavedImage;
                this.IconPath = activityModel.IconPath;
                this.ItemIcon = activityModel.ItemIcon;
                this.IsLatest = activityModel.IsLatest;
                this.Version = activityModel.Version;
                activityModel.InputParameters.ForEach(item => this.InputParameters.Add(new ParameterModel(item)));
                activityModel.OutputParameters.ForEach(item => this.OutputParameters.Add(new ParameterModel(item)));

                if (null == activityModel.RelatedWorkflows)
                {
                    this.RelatedWorkflows = null;
                }
                else
                {
                    this.RelatedWorkflows = new ObservableCollection<WorkflowViewModel>();
                    activityModel.RelatedWorkflows.ForEach(item => this.RelatedWorkflows.Add(new WorkflowViewModel(item)));
                }
            }
        }

        #endregion Constructors

        #region Public Properties

        /// <summary>
        ///  A flag indication if the workflow is deleted.
        /// </summary>
        public bool IsDeleted
        {
            get
            {
                return isDeleted;
            }
            set
            {
                isDeleted = value;
                this.RaisePropertyChanged("IsDeleted");
            }
        }

        /// <summary>
        /// Gets or sets the parent model.
        /// </summary>
        public override BaseRegMgrModel Parent
        {
            get
            {
                return base.Parent;
            }
            set
            {
                base.Parent = value;
                if (this.Parent != null)
                {
                    this.ParentBreadcrumb = (null == this.Parent) ? string.Empty : (this.Parent as CategoryViewModel).CategoryBreadcrumb;
                }
            }
        }

        /// <summary>
        /// The path of the parent bread crumb.
        /// </summary>
        public string ParentBreadcrumb
        {
            get
            {
                return this.parentBreadcrumb;
            }

            set
            {
                this.parentBreadcrumb = value;
                this.RaisePropertyChanged("ParentBreadCrumb");
            }
        }
        
        /// <summary>
        /// Gets or sets the author.
        /// </summary>
        public string Author
        {
            get
            {
                return this.author;
            }
            set
            {
                this.author = value;
                this.RaisePropertyChanged("Author");
            }
        }

        /// <summary>
        /// Gets or sets the keywords.
        /// </summary>
        public string Keywords
        {
            get
            {
                return this.keywords;
            }

            set
            {
                this.keywords = value;
                this.RaisePropertyChanged("Keywords");
            }
        }

        /// <summary>
        /// Gets or sets the icon path.
        /// </summary>
        /// <value>The icon path.</value>
        public string IconPath
        {
            get
            {
                return string.IsNullOrEmpty(this.iconPath) ? "Trident Icon" : this.iconPath;
            }

            set
            {
                string newPath = value;
                string oldPath = this.IconPath;
                if (File.Exists(newPath))
                {
                    try
                    {
                        BitmapImage image = new BitmapImage();
                        image.BeginInit();
                        image.CacheOption = BitmapCacheOption.OnLoad;
                        image.UriSource = new Uri(newPath, UriKind.RelativeOrAbsolute);
                        image.DecodePixelWidth = Constants.BROWSEICONWIDTH;
                        image.EndInit();
                        this.PreviewImage = image;
                        this.iconPath = newPath;
                    }
                    catch (NotSupportedException ex)
                    {
                        TridentErrorHandler.HandleKnownExceptionsInUI(ex,
                            ManagementStudioResourceManager.GetString("InvalidImage"));
                        this.IconPath = oldPath;
                    }
                }

                this.RaisePropertyChanged("IconPath");
            }
        }

        /// <summary>
        /// Gets or sets the icon.
        /// </summary>
        /// <value>The icon.</value>
        public Image Icon
        {
            get { return icon; }
            set
            {
                icon = value;
                this.RegistryImage = ActivityViewModel.BitmapImageFromSDCtrl(this.Icon);
                this.RaisePropertyChanged("Icon");
            }
        }

        /// <summary>
        /// Gets or sets the preview image.
        /// </summary>
        /// <value>The preview image.</value>
        public BitmapImage PreviewImage
        {
            get
            {
                return this.previewImage != null ? this.previewImage : (BitmapImage)DependencyProperty.UnsetValue;
            }
            set
            {
                this.previewImage = value;
                if (this.previewImage != null && this.previewImage.CanFreeze)
                {
                    this.previewImage.Freeze();
                }

                this.RaisePropertyChanged("PreviewImage");
            }
        }

        /// <summary>
        /// Gets or sets the registry image.
        /// </summary>
        /// <value>The registry image.</value>
        public BitmapImage RegistryImage
        {
            get
            {
                return this.registryImage != null ? this.registryImage : (BitmapImage)DependencyProperty.UnsetValue;
            }
            set
            {
                this.registryImage = value;
                this.PreviewImage = null;
                if (this.registryImage != null && this.registryImage.CanFreeze)
                {
                    this.registryImage.Freeze();
                }

                this.RaisePropertyChanged("RegistryImage");
            }
        }

        /// <summary>
        /// Gets or sets the saved image.
        /// </summary>
        /// <value>The saved image.</value>
        public BitmapImage SavedImage
        {
            get
            {
                return this.savedImage != null ? this.savedImage : ActivityViewModel.DefaultImage;
            }
            set
            {
                this.savedImage = value;
                if (this.savedImage != null && this.savedImage.CanFreeze)
                {
                    this.savedImage.Freeze();
                }

                this.RaisePropertyChanged("SavedImage");
                this.RaisePropertyChanged("ItemIcon");
            }
        }

        /// <summary>
        /// Gets or sets the saved image.
        /// </summary>
        /// <value>The saved image.</value>
        public override BitmapImage ItemIcon
        {
            get
            {
                return this.SavedImage;
            }
            set
            {
                this.SavedImage = value;
            }
        }
        
        /// <summary>
        /// The default image.
        /// </summary>
        public static BitmapImage DefaultImage
        {
            get
            {
                if (null == ActivityViewModel.defaultImage)
                {
                    ActivityViewModel.CreateDefaultImage();
                }

                if (null != ActivityViewModel.defaultImage)
                {
                    return ActivityViewModel.defaultImage;
                }
                else
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// Gets the collection of input parameters.
        /// </summary>
        public ObservableCollection<ParameterModel> InputParameters
        {
            get
            {
                return this.inputParameters;
            }
        }

        /// <summary>
        /// Gets the collection of output parameters.
        /// </summary>
        public ObservableCollection<ParameterModel> OutputParameters
        {
            get
            {
                return this.outputParameters;
            }
        }

        /// <summary>
        /// Gets a value indicating if there are any input parameters or not.
        /// </summary>
        public bool HasInputParameters
        {
            get
            {
                return this.InputParameters.Count > 0;
            }
        }

        /// <summary>
        /// Gets a value indicating if there are any input parameters or not.
        /// </summary>
        public bool HasNoInputParameter
        {
            get
            {
                return !this.HasInputParameters;
            }
        }
        
        /// <summary>
        /// Gets a value indicating if there are any output parameters or not.
        /// </summary>
        public bool HasOutputParameters
        {
            get
            {
                return this.OutputParameters.Count > 0;
            }
        }

        /// <summary>
        /// Gets a value indicating if there are any output parameters or not.
        /// </summary>
        public bool HasNoOutputParameters
        {
            get
            {
                return !this.HasOutputParameters;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has related workflows.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has related workflows; otherwise, <c>false</c>.
        /// </value>
        public bool HasRelatedWorkflows
        {
            get
            {
                bool hasRelatedWfs = false;
                if (this.RelatedWorkflows != null && this.RelatedWorkflows.Count > 0)
                {
                    hasRelatedWfs = true;
                }

                return hasRelatedWfs;
            }
        }

        /// <summary>
        /// Gets a value indicating whether this instance has no related workflows.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance has no related workflows; otherwise, <c>false</c>.
        /// </value>
        public bool HasNoRelatedWorkflows
        {
            get
            {
                return !this.HasRelatedWorkflows;
            }
        }

        /// <summary>
        /// Gets or sets the related workflows. Gets NULL if the collection is not initialized.
        /// </summary>
        /// <value>
        /// The related workflows.
        /// </value>
        public ObservableCollection<WorkflowViewModel> RelatedWorkflows
        {
            get;
            set;
        }

        #endregion

        #region Methods
        
        /// <summary>
        /// Creates a clone of the passed model.
        /// </summary>
        /// <param name="activityViewModel">
        /// Model to clone.
        /// </param>
        /// <returns>
        /// A cloned version.
        /// </returns>
        public static ActivityViewModel CreateClone(ActivityViewModel activityViewModel)
        {
            ActivityViewModel clonedModel = null;
            if (activityViewModel != null)
            {
                clonedModel = new ActivityViewModel(activityViewModel);
            }

            return clonedModel;
        }

        /// <summary>
        /// Overridden tostring method. For automation Ids.
        /// </summary>
        /// <returns>The string representation of the object.</returns>
        public override string ToString()
        {
            if (!string.IsNullOrEmpty(this.Label))
            {
                return this.Label;
            }
            return base.ToString();
        }
        
        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            bool equal = false;
            ActivityViewModel equateParam = obj as ActivityViewModel;
            if (equateParam != null &&
                equateParam.Id.Equals(this.Id) &&
                ActivityViewModel.CheckEquality(equateParam.Label, this.Label) &&
                ActivityViewModel.CheckEquality(equateParam.Author, this.Author) &&
                ActivityViewModel.CheckEquality(equateParam.Keywords, this.Keywords) &&
                ActivityViewModel.CheckEquality(equateParam.Description, this.Description) &&
                ActivityViewModel.CheckEquality(equateParam.Icon, this.Icon) &&
                ActivityViewModel.CheckEquality(equateParam.IconPath, this.IconPath) &&
                ActivityViewModel.CheckInputParamsEquality(equateParam, this) &&
                ((equateParam.Parent == null && this.Parent == null) ||
                (equateParam.Parent != null && this.Parent != null && equateParam.Parent.Id.Equals(this.Parent.Id))))
            {
                equal = true;
            }

            return equal;
        }

        /// <summary>
        /// Updates the specified saved activity.
        /// </summary>
        /// <param name="savedActivity">The saved activity.</param>
        public void Update(SR.Activity activity)
        {
            this.Id = ((SR.IObject)activity).ID;
            this.Name = activity.Name;
            this.Author = activity.Author;
            this.Label = string.IsNullOrEmpty(activity.Label) ? activity.Name : activity.Label;
            this.Description = activity.Description;
            this.CreatedBy = activity.Created.User;
            this.CreatedDate = activity.Created.When;
            this.LastModifiedTime = activity.Updated.When;
            this.isDeleted = activity.IsDeleted;
            this.LastModifiedBy = activity.Updated.User;
            this.LastModifiedTime = activity.Updated.When;
            this.Icon = activity.Icon;
            this.SavedImage = ActivityViewModel.BitmapImageFromSDCtrl(activity.Icon);
            this.Keywords = activity.Keywords;

            this.InputParameters.Clear();
            this.OutputParameters.Clear();

            activity.ActivityParameters
                .Where(item => item.Direction == SR.DirectionType.Input)
                .ForEach(item => this.InputParameters.Add(new ParameterModel(item)));

            activity.ActivityParameters
                .Where(item => item.Direction == SR.DirectionType.Output)
                .ForEach(item => this.OutputParameters.Add(new ParameterModel(item)));
        }

        /// <summary>
        /// Updates the specified model.
        /// </summary>
        /// <param name="model">The model.</param>
        public override void Update(BaseRegMgrModel model)
        {
            ActivityViewModel activityModel = model as ActivityViewModel;
            if (activityModel != null)
            {
                this.Name = activityModel.Name;
                this.Author = activityModel.Author;
                this.Label = activityModel.Label;
                this.Description = activityModel.Description;
                this.CreatedDate = activityModel.CreatedDate;
                this.CreatedBy = activityModel.CreatedBy;
                this.LastModifiedTime = activityModel.LastModifiedTime;
                this.LastModifiedBy = activityModel.LastModifiedBy;
                this.ItemIcon = activityModel.ItemIcon;
                this.Icon = activityModel.Icon;
                this.IconPath = activityModel.IconPath;
                this.IsDeleted = activityModel.IsDeleted;
                this.Keywords = activityModel.Keywords;
                this.ParentBreadcrumb = activityModel.ParentBreadcrumb;
                for (int iterator = 0; iterator < activityModel.InputParameters.Count; iterator++)
                {
                    this.InputParameters[iterator].Update(activityModel.InputParameters[iterator]);
                }

                foreach (BaseRegMgrModel child in activityModel.Children)
                {
                    BaseRegMgrModel currentChild = child.GetModelById(child, this.Children);
                    if (currentChild != null)
                    {
                        currentChild.Update(child);
                    }
                }

                this.IsSelected = activityModel.IsSelected;
            }
        }

        private static bool CheckInputParamsEquality(ActivityViewModel model1, ActivityViewModel model2)
        {
            bool equal = true;
            if (model1.InputParameters.Count == model2.InputParameters.Count)
            {
                for (int iterator = 0; iterator < model1.InputParameters.Count; iterator++)
                {
                    equal = model1.InputParameters[iterator].Equals(model2.InputParameters[iterator]);
                    if (!equal)
                    {
                        break;
                    }
                }
            }
            else
            {
                equal = false;
            }

            return equal;
        }

        private static BitmapImage BitmapImageFromSDCtrl(Image img)
        {
            BitmapImage bitmapImg = null;
            if (img != null)
            {
                if (!ActivityViewModel.imageToBitmapDictionary.TryGetValue(img, out bitmapImg))
                {
                    try
                    {
                        using (MemoryStream memStream = new MemoryStream())
                        {
                            img.Save(memStream, System.Drawing.Imaging.ImageFormat.Png);
                            bitmapImg = new BitmapImage();
                            bitmapImg.BeginInit();
                            bitmapImg.CacheOption = BitmapCacheOption.OnLoad;
                            bitmapImg.StreamSource = memStream;
                            bitmapImg.EndInit();
                        }

                        if (null == bitmapImg)
                        {
                            bitmapImg = ActivityViewModel.DefaultImage;
                        }
                        else
                        {
                            ActivityViewModel.imageToBitmapDictionary[img] = bitmapImg;
                        }
                    }
                    catch (Exception)
                    {
                        bitmapImg = ActivityViewModel.DefaultImage;
                    }
                }
            }
            else
            {
                bitmapImg = ActivityViewModel.DefaultImage;
            }

            return bitmapImg;
        }

        private static void CreateDefaultImage()
        {
            try
            {
                Uri uri = new Uri(@"pack://Application:,,,/Microsoft.Research.ScientificWorkflow.TridentUtilities;component/Images/ManagementStudio/ActivitesIcon.png", UriKind.Absolute);
                ActivityViewModel.defaultImage = new BitmapImage(uri);
                if (ActivityViewModel.defaultImage != null && ActivityViewModel.defaultImage.CanFreeze)
                {
                    ActivityViewModel.defaultImage.Freeze();
                }
            }
            catch (UriFormatException ex)
            {
                TridentErrorHandler.HandleUIException(ex);
            }
            catch (ArgumentException ex)
            {
                TridentErrorHandler.HandleUIException(ex);
            }
            catch (Exception ex)
            {
                TridentErrorHandler.HandleUIException(ex);
            }
        }

        #endregion Methods
    }
}
